#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
/*
测试父子进程的消息队列使用
*/
int main()
{
    // 在/dev/mqueue下生成对应文件（消息保存在内存队列中，并不保存在此文件中）
    char *mq_name = "/father_son_mq";

    // 定义配置队列的struct
    struct mq_attr attr;
    attr.mq_maxmsg = 10;   // 设置消息队列可以容纳的最大消息数
    attr.mq_msgsize = 100; // 设置容许的单条消息最大长度

    // 创建消息队列
    mqd_t mqdes = mq_open(mq_name, O_CREAT | O_RDWR, 0666, &attr);

    if (mqdes == (mqd_t)-1)
    {
        perror("创建消息队列失败");
        return 1;
    }

    // 创建子进程
    pid_t pid = fork();
    if (pid > 0) // 父进程
    {
        char send_buf[100]; // 用于存放要发送数据的缓冲区
        struct timespec time_spec;

        for (int i = 0; i < 10; i++) // 循环10,向队列中发送10次消息
        {
            // 清空发送缓冲区
            memset(send_buf, 0, 100);

            // 得到当前时间5秒后时间
            clock_gettime(0, &time_spec);
            time_spec.tv_sec += 5;
            // 将要发送的数据写入sendBuf中
            sprintf(send_buf, "父进程的第 %d 次发送\n", (i + 1));

            // 向队列发送定时消息数据
            if (mq_timedsend(mqdes, send_buf, strlen(send_buf), 0, &time_spec) == -1)
            {
                perror("发送消息失败");
                return 1;
            }

            printf("父进程发送完毕，休眠1s\n");

            sleep(1); // 每次发送之间休眠1秒
        }
    }
    else if (pid == 0) // 子进程
    {
        char read_buf[100];        // 用于存储接收到的消息数据的缓冲区
        struct timespec time_spec; // 用于存放时间的结构体

        for (int i = 0; i < 10; i++)
        {
            // sleep(2);

            // 清空接收缓冲区
            memset(read_buf, 0, 100);

            // 得到当前时间15秒后时间 =>用于指定阻塞等待的最长时间
            clock_gettime(CLOCK_REALTIME, &time_spec);
            time_spec.tv_sec += 15;

            // 读取出队列中的数据并打印到控制台
            int result = mq_timedreceive(mqdes, read_buf, 100, NULL, &time_spec);
            if (result != -1)
            {
                printf("子进程读取到数据: %s\n", read_buf);
            }
            else
            {
                perror("mq_timedreceive");
                return 1;
            }
        }
    }

    // 无论是父进程还是子进程都应该关闭队列描述符，只有引用全部释放，消息队列才会被真正销毁
    close(mqdes);

    // 清除消息队列（只应执行一次）
    if (pid == 0)
    {
        mq_unlink(mq_name);
    }

    return 0;
}